﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Example7_Collisions.Model;

namespace Example7_Collisions.Model
{
    class Model
    {

        class CollisionDetails
        {
            public Vector2 m_speedAfterCollision;
            public Vector2 m_positionAfterCollision;
            public bool m_hasCollidedWithGround = false;
            private Vector2 a_oldPos;
            private Vector2 a_velocity;

            public CollisionDetails(Vector2 a_oldPos, Vector2 a_velocity)
            {
                m_positionAfterCollision = a_oldPos;
                m_speedAfterCollision = a_velocity;
            }

        }

        const float m_rightEdge = 1;

        private Level m_level = new Level();
        private Player m_player = new Player();

        bool m_hasCollidedWithGround = false;

        internal void Update(float a_elapsedTime)
        {
            float timeStep = 0.001f;
            if (a_elapsedTime > 0)
            {
                int numIterations = (int)(timeStep / a_elapsedTime);

                for (int i = 0; i < numIterations; i++)
                {
                    UpdatePlayer(timeStep);
                }

                float timeLeft = a_elapsedTime - timeStep * numIterations;
                UpdatePlayer(timeLeft);
            }

        }

        private void UpdatePlayer(float a_elapsedTime)
        {
            //Get the old position
            Vector2 oldPos = m_player.GetPosition();

            //Get the new position
            m_player.Update(a_elapsedTime);

            Vector2 newPos = m_player.GetPosition();

            //Collide
            m_hasCollidedWithGround = false;
            Vector2 speed = m_player.GetSpeed();

            if (didCollide(newPos, m_player.m_sizes))
            {
                CollisionDetails details = getCollisionDetails(oldPos, newPos, m_player.m_sizes, speed);
                m_hasCollidedWithGround = details.m_hasCollidedWithGround;

                //set the new speed and position after collision
                m_player.SetPosition(details.m_positionAfterCollision);
                m_player.SetSpeed(details.m_speedAfterCollision);
            }
            

            
        }

        private bool didCollide(Vector2 a_centerBottom, Vector2 a_size)
        {
             FloatRectangle occupiedArea = FloatRectangle.createFromCenterBottom(a_centerBottom, a_size);
             if (m_level.IsCollidingAt(occupiedArea))
             {
                 return true;
             }
             return false;
        }

        
        private CollisionDetails getCollisionDetails(Vector2 a_oldPos, Vector2 a_newPosition, Vector2 a_size, Vector2 a_velocity)
        {
            CollisionDetails ret = new CollisionDetails(a_oldPos, a_velocity);

            Vector2 slidingXPosition = new Vector2(a_newPosition.X, a_oldPos.Y); //Y movement ignored
            Vector2 slidingYPosition = new Vector2(a_oldPos.X, a_newPosition.Y); //X movement ignored

            if (didCollide(slidingXPosition, a_size) == false)
            {
                return doOnlyXMovement(ref a_velocity, ret, ref slidingXPosition);
            }
            else if (didCollide(slidingYPosition, a_size) == false)
            {
                
                return doOnlyYMovement(ref a_velocity, ret, ref slidingYPosition);
            }
            else
            {
                return  doStandStill(ret, a_velocity);
            }
           
        }

        private static CollisionDetails doStandStill(CollisionDetails ret, Vector2 a_velocity)
        {
            if (a_velocity.Y > 0)
            {
                ret.m_hasCollidedWithGround = true;
            }
            ret.m_speedAfterCollision = new Vector2(0, 0);
            return ret;
        }

        private static CollisionDetails doOnlyYMovement(ref Vector2 a_velocity, CollisionDetails ret, ref Vector2 slidingYPosition)
        {
            a_velocity.X *= -0.5f; //bounce from wall
            ret.m_speedAfterCollision = a_velocity;
            ret.m_positionAfterCollision = slidingYPosition;
            return ret;
        }

        private static CollisionDetails doOnlyXMovement(ref Vector2 a_velocity, CollisionDetails ret, ref Vector2 slidingXPosition)
        {
            ret.m_positionAfterCollision = slidingXPosition;
            //did we slide on ground?
            if (a_velocity.Y > 0)
            {
                ret.m_hasCollidedWithGround = true;
            }

            ret.m_speedAfterCollision = doSetSpeedOnVerticalCollision(a_velocity);
            return ret;
        }

        private static Vector2 doSetSpeedOnVerticalCollision(Vector2 a_velocity)
        {
            //did we collide with ground?
            if (a_velocity.Y > 0)
            {
                a_velocity.Y = 0; //no bounce
            }
            else
            {
                //collide with roof
                a_velocity.Y *= -1.0f;
            }

            a_velocity.X *= 0.10f;

            return a_velocity;
        }
               

        

        internal bool CanJump()
        {
            return m_hasCollidedWithGround;
        }

        internal void DoJump()
        {
            if (CanJump())
            {
                m_player.DoJump();
            }
        }


        internal Level GetLevel()
        {
            return m_level;
        }

        internal Vector2 GetPlayerPosition()
        {
            return m_player.GetPosition();
        }

        internal float GetPlayerSpeed()
        {
            return m_player.GetSpeed().Length();
        }

        internal void MoveLeft()
        {
            m_player.SetSpeed( new Vector2(-5.0f, m_player.GetSpeed().Y ) );
        }

        internal void MoveRight()
        {
            m_player.SetSpeed(new Vector2(5.0f, m_player.GetSpeed().Y));
        }
    }
}
